/*

	Frothjs Library	&& fClass Runtime Environment
	=============================================
	
	Created: Tues, Oct 20, 2009
	Created By: Allan Phillips
	
	@copyright 2009 Thinking Code Software. All Rights Reserved
	
	Released under the MIT License (See ../LICENSE)
	
	Changes:
	1.0.0 	- Oct 20, 2009 	- Added fClass with base NSObject/NSModelObject library support
	1.0.1	- Oct 21, 2009 	- FIXED Incompatibility with Firefox 3.5.1, and conditional checking for window.console for NSLog
	1.0.2	- Oct 23, 2009	- FIXED Issues with addObserverForKey(akey, observer) and obverver.observeChange()
	2.0.0	- Feb 24, 2010	- Rebuild fClass runtime removing reliance on -eval()
	
	2.0.0 Issues:
	- performSuper() is not fully bug free, it has the following known issues
		1+ 	Calls to a super impl when the calling instance's class does not contain a definition
			(ie does not overide) of the method will cause a failure (cannot find super's impl)
		2+	Issue with WBView when its 'init' convienence initializer is implemented cuases an endless loop between
			initWithTag and init when the initalized with initWithTag (this only appears to occurs with WBControl)
	
	Files:
	froth.core.js 	 		- Provides the push based integration with froth.
	froth.uikit.js 			- Provides uikit for drawing views with froth
	froth.{plugin}.js 		- Some plugin for froth.
	
	Background
	=============================================
	
	Provides an object orinted runtime for interacting with frothkit and
	doing cool web things.
	

	Examples
	=============================================

	1. Define a class (must inherit from NSObject)
	----------------------------------------------
	
	fClass.NSObject.extend("THSubclass", { ivars:{
	}, methods:{
	
	doThis:function(withThat) {
		this 			== The instance (self)
		this._class 	== Object's Class
		this.className 	== Object's Class name
		this.superof	== Object's super class impl
		
		//Calling a super impl		
		this.performSuper(this, "doSomeSuperMethod", arguments);
		
		this.myVal = "that";
	}
	
	}}});
	
	2. Initalize a instance of a class
	----------------------------------------------
	
	var theInstance = fClass.THSubclass.alloc().init();
	theInstance.doThis("someVal");
	
	3. Add a Class Catagory
	
	fClass.THSubclass.add({ methods:{
		
	someCatagoryMethod:function() {
		//...
	}
	
	}});
	
	3. Special Class Methods (Non inherited)
	----------------------------------------------
	
	Classes can implement the class method
	
	+onDescended(decendent) 
	Used to track when a class has been subclassed by itself of any of its subclassed with extend().
	All ancestors that have +onDescended() implemented are called, no calls to super are needed
	
	+initialize()
	Every class is called this once (if implemented) at the end of the .extend call and can be used to setup class
	specific details. It is not inheritable.
	
*/

/*
	Global Vars and Functions for Runtime
	=====================================
	
	NSLog(str) 				- Provides a log for console messages
	NSErrorLog(str)			- Provides an error log
	
	
	fClass 					- The main namespace for the fClass runtime.
	c_ 						- A Refereance to fClass
	
	indexof(obj, inArr)		- Provides the ability get index of object in array
	removefrom(obj, inArr)	- Provides the ability to remove an object (return a new array with removal.
	
	String.deCamelize		- Provides converting ThisCamelCase => this_camel_case, use by the runtime
	String.startsWith		- Pretty self explaintory
	String.endsWith			- ditto..
*/

function removefrom(object, array) {
	var index;
	for(index in array) {
		if(array[index] == object)
			break;
	}
	return array.splice(index, 1);
}

function indexof(object, array) {
	for(index in array) {
		if(array[index] == object)
			return index;
	}
	return -1;
}

function copyof(object) {
	var copy = [];
	for(var index in object) {
		copy[index] = object[index];
	}
	return copy;
}

function ArrayContains(array, object) {
	if(indexof(object, array) >= 0)
		return true;
	return false;
}

//Returns a new array containing any object from arrayOne not in arrayTwo
function diffFromArrayNotIn(arrayOne, arrayTwo) {
	//window.console.error(arrayOne);
	//window.console.error(arrayTwo);
	var newArr = [];
	for(var i in arrayOne) {
		if(!arrayTwo || !(arrayTwo.length > 0) || !ArrayContains(arrayTwo, arrayOne[i])) {
			newArr.push(arrayOne[i]);
		}	
	}
	return newArr;
}

String.prototype.deCamelize = function() { return this.replace(/([A-Z]+)/g, '_$1').toLowerCase().replace(/^_/, ''); }
String.prototype.deCamelizeWithSpaces = function() { return this.replace(/([A-Z])/g, ' $1'); }

if ( !(new String).trim ){
	String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,''); };
}	

if ( !(new String).normalize && (new String).trim ){
	String.prototype.normalize = String.prototype.normalise = function() { return this.trim().replace(/\s+/g,' '); };
}

if ( !(new String).startsWith ){
	String.prototype.startsWith = function(str,i){ i=(i)?'i':'';var re=new RegExp('^'+str,i);return (this.normalize().match(re)) ? true : false ; };
}	

if ( !(new String).endsWith ){
	String.prototype.endsWith = function(str,i){ i=(i)?'gi':'g';var re=new RegExp(str+'$',i);return (this.normalize().match(re)) ? true : false ; };
}

var NSLog = function(str) { if(window.console) { window.console.log("(frothjs) - " + str); } };
var NSErrorLog = function(str) { if(window.console) { window.console.error("(frothjs) - " + str); } };

// !fClass 2.0
/*
	
	fClass 2.0
	===================================
	
	Updated runtime

*/

//defualt namespace
fClass = {};

//Shortcut
c_ = fClass;

// Holds actual js classes
fClass.prototypes = {};

fClass._version = '2.0.0';
fClass._instanceCount = 0;
fClass.nextInstanceCount = function() {
	this._instanceCount = this._instanceCount + 1;
	return this._instanceCount;
};

// !Extend Method
fClass.extend = function(subclassName, definition) {
	
	if(this != fClass)
		var superInstance = this;
	
	fClass.prototypes[subclassName] = function() {
		
		this.superof = superInstance;
		this.className = subclassName;
	
		
		//Add definition logic
		if(definition.ivars) {
			for(var pname in definition.ivars) {
				if(typeof definition.ivars[pname] != 'function') {
					if($.isArray(definition.ivars[pname])) {
						this[pname] = copyof(definition.ivars[pname]);	//We need to sanize the property.
					} else {
						this[pname] = definition.ivars[pname];
					}
				}
			}
		}
		if(definition.methods) {
			for(var mname in definition.methods) {
				if(typeof definition.methods[mname] == 'function') {
					this[mname] = definition.methods[mname];
				}
			}
		}
		
		if(definition.classMethods) {
			for(var mname in definition.classMethods) {
				if(typeof definition.classMethods[mname] == 'function') {
					this[mname] = definition.classMethods[mname];
				}
			}
		}
		
		
		/*
			Base alloc implementation
			--------------------------
			
			This basically just makes a copy of our prototype implementation and returns it,
			by defining it here, we insure that it cannot be overidden
			
			Also defines the 'Class method as well as provides support for class methods.
		*/
		this.alloc = function() {
			var instance = (new fClass.prototypes[subclassName]());
				
				
			//Delete class methods...		
			instance.alloc = function() { throw 'Instances must not have [+alloc] called' };	//dont allow extra allocs
			instance.extend = function() { throw 'Instances must not have [+extend] called' };	//dont allow instances to be extended.
			
			instance._class = fClass[subclassName];
			instance._definition = definition;
			
			return instance;
		}
	}
	
	//Inheret super via prototype mech.
	if(this != fClass)
		fClass.prototypes[subclassName].prototype = this;
	
	//Create the class instance (for use with -alloc() -init())
	fClass[subclassName] = (new fClass.prototypes[subclassName]());
	fClass[subclassName].extend = fClass.extend;
	
	//Call the various class method
	if(fClass[subclassName]['initialize']) {
		fClass[subclassName]['initialize']();
	}
	if(this.onDecended) {
		this.onDecended(fClass[subclassName]);
	}
}

/*

	NSObject
	===================================
	
	All subclasses inherit from NSObject
	
	Note that NSObject is constucted differently the the standard Class.extend() method.
	This is becouse it implements special functions, and methods for doing Class.extend

*/

// !NSObject Root Class
fClass.extend('NSObject', { ivars : {

_hash:""	

}, methods: {

/*
	Instance Methods
	----------------
*/
init:function() {
	this._hash = this.className+":"+fClass.nextInstanceCount();
	return this;
},
printClassTree:function() {
	if(this.superof != null) {
		window.console.log(this.className + ':' + this.superof.className);
		this.superof.printClassTree();
	} else {
		window.console.log(this.className+':ROOT');
	}
},

respondsToSelector:function(method) {
	return (typeof this[method] == 'function');
},

performSelector:function(method, args) {
	if(this.respondsToSelector(method)) {
		return this[method].apply(this, args);
	} else {
		throw "["+this.className+"] does not implement the method:["+method+"]";
	}
},

performSuper:function(ctx, method, args) {	
	var result;
	
	if(!ctx._lastCtx) {
		ctx._lastCtx = ctx.superof;
		ctx._lastCall = ctx[method];
		//NSLog('+initalizing call trace super:'+ctx._lastCtx.className+ ' method:'+method);
	}
	
	var useSelfs = false;
	//Make sure its a super impl, not an inherited impl
	while(ctx._lastCtx.superof && ctx._lastCtx[method] == ctx._lastCall) {
		/*if(ctx._lastCtx == ctx.superof && !ctx._definition.methods[method]) {
			useSelfs = true;
		}*/
		ctx._lastCtx = ctx._lastCtx.superof;
	}
	
	/*
		We found the super with a different impl, lets make that our next call.
		This always insure that nested calles to performSuper start at this last
		call.
		
		If this is the first level down from self, then we need to check if its inherited or not
	*/
	
	//if(!useSelfs)
		ctx._lastCall = ctx._lastCtx[method];
	
	//Make sure its actually a function
	//NSLog('perform next:'+ctx._lastCtx.className+' function:'+ctx._lastCall);
	
	if(typeof ctx._lastCall == 'function') {
		result = ctx._lastCall.apply(ctx, args);
	} else {
		ctx._lastCtx = null;
		ctx._lastCall = null;
		var superClassName = ctx.superof?ctx.superof.className:'ROOT';
		throw "["+superClassName+"]["+ctx.className+"] does not implement or inherit the method:["+method+"]";
	} 
	
	//Now clean up, only if _lastCall == final impl in inheritance tree
	//if(ctx._isTopImpl(ctx._lastCall)) {
		//NSLog('+finalizing call trace');
		ctx._lastCtx = null;
		ctx._lastCall = null
	//}
	
	return result;
},

/* Returns the namespace path of the object (NSObject.NSWidget.MyObject.MyOtherObject) */
fnamespace:function() {
	var namespace = this.className.substring(2).deCamelize();
	var s = this.superof;
	while(s) {
		namespace = s.className.substring(2).deCamelize().concat("."+namespace);
		s = s.superof;
	}
	return namespace;
},

classNamespace:function() {
	var namespace = this.className;
	var s = this.superof;
	while(s) {
		namespace = s.className.concat("."+namespace);
		s = s.superof;
	}
	return namespace;
},

/* Returns a copy of self (note that scope fails for this with "self") */
mutableCopy:function() {
	var obj = {};
	for(var key in this) {
		obj[key] = this[key];
	}
	
	//Update the hash
	obj._hash = this.className+":"+fClass.nextInstanceCount();
	
	return obj;
},

valueForKey:function(key) {
	this.willAccessValueForKey(key);
	
	var val = null;
	if(typeof this[key] == 'undefined') {
		throw("Froth object:"+this.className+" does not have a value for key: " +key);
		return null;
	} else {
		val = this[key];
	}
	
	this.didAccessValueForKey(key);
	return val;
},

willAccessValueForKey:function(key) {
	//Subclasses can do something with this.
},

/* Sent after a value is returned to caller for -valueForKey(key). only if value is not "undefined" */
didAccessValueForKey:function(key) {
	//Subclasses can do something with this.
},

/* Set a value for given key. This also fires -didChangeValueForKey(key) and -willChangeValueForKey(key) */
setValueForKey:function(value, key) {
	this.willChangeValueForKey(key);
	var oldVal = this[key];
	if(oldVal && oldVal != value) {
		this[key] = value;
		this.didChangeValueForKey(key);
	}
},

/* Triggered before a value is set with -didChangeValueForKey(key) */
willChangeValueForKey:function(key) {
	//Subclasses can do something interesting here..
},

/* Triggered after a value is set, ONLY if it was actually a new value. This also sends a notification to observers
	observers can register with.
	$(document).bind("<key>.changed.<className>", function(e, self, key) { do something with self&changed key }
 
	This is seperate to Froth Page Bindings
 
	Subclasses should call super's implementation if they overide
 
	Becouse of namespacing rules, we could also just do $.bind("<akey>", ... however if multiple objects have the same key, then all will be bound
 */
didChangeValueForKey:function(akey) {
	$(document).trigger(akey+'_changed_'+this.className+"_ins"+this._hash, [this, akey]);
},

/* 
	Adds the ability to bind a function to a kvo change, the function should be function(event, self, key) 
	For inline funtions, also apply a content will insure that -removeObser... will also work
*/
addObserverFunctionForKey:function(aFunction, key, context) {
	if(context) 
		$(document).bind(key+'_changed_'+this.className+"_ins"+this._hash+"."+context, aFunction);
	else
		$(document).bind(key+'_changed_'+this.className+"_ins"+this._hash, aFunction);
},

/* Remove a previously bound function for given key */
removeObserverFunctionForKey:function(aFunction, key, context) {
	if(context)
		$(document).unbind(key+'_changed_'+this.className+"_ins"+this._hash+"."+context, aFunction);
	else
		$(document).unbind(key+'_changed_'+this.className+"_ins"+this._hash, aFunction);
},

/*
	We need this, as passing in a function directly to jquery will
	cause its scope to change where the this will be (document)
*/
_privateObserveChange:function(e, observed, key) {
	e.data.observeChange(observed, key);	//e.data == observer who called addObserverForKey(observer...
},

/* Allows one Froth object to observe changes of another object. 
	On change, the observer will receive a.
 
	This must be implemented by observer or super, as Froth returns a exception in its implementation
	-observeChange(observedObject, key)
 */
addObserverForKey:function(observer, key) {
	$(document).bind(key+'_changed_'+this.className+"_ins"+this._hash+"."+observer._hash, observer, this._privateObserveChange);
},

/* 
	Removes an observer previously added
 */
removeObserverForKey:function(observer, key) {
	$(document).unbind(key+'_changed_'+this.className+"_ins"+this._hash+"."+observer._hash, this._privateObserveChange);
},

/* Base method. subclasses must overide and not ever call Froth's implementation or an exception is thrown */
/*observeChange:function(event, object, key) {
	throw("Subclasses that observer changes with addObserverForKey must never call Froth's implementation, or not implement unless a super has implemented -observeChange()");
}*/


}, classMethods: { //@end

/*
	Class Methods
	--------------
*/

/* 
	
	All subclasses can implement the +handlePush class method used by froth xmpp
	+handlePush(fx, namespace);

	Internally this is handled by this class, as the froth.push.js file will
	call NSObject.handlePush(fx, namespace), which then from here gets routed
	to the appropriate class corresponding to namespace rules.
	
	Namespace to Class Mapping Rules
	1. Absolute Mapping
	froth:NSObject:DJEnv:DJRuntime will check if DJRuntime implements +handlePush, if not, DJEnv is checked
	finally NSObject is skiped, as that is this impl.
	
	2. Model Object Mapping.
	froth:model:[entity-name] will check for NSModelObject.entities[entity-name].handlePush, where
	[entity_name] == PFEntityName

*/
handlePush:function(fx, namespace) {
	//Insure that we are not called as a result of -super
	if(this.className != 'NSObject') {
		//throw "this.superClass.handlePush() should never be called when superClass is NSObject";
		return null;
	}
	
	var classFullName = namespace.replace("froth:", "").replace(/\:/, ".");
	
	//1. Now we potentionally have model. or XXSomething.
	if(classFullName.startsWith("model.")) {
		classFullName = classFullName.replace("model.", "");
		
		//We should now have that classFullName  == entity for NSModelObject subclasss
		if(fClass.NSModelObject.entities[classFullName].handlePush) {
			fClass.NSModelObject.entities[classFullName].handlePush(fx, namespace);
		}
		
	} else {
		var subclass = this.classForNamespace(classFullName);
		if(subclass.handlePush) {	//Always implements as -handlePush is being inhereted. we need to program that out.
			subclass.handlePush(fx, namespace);
		} else {
			NSErrorLog("FROTH ++ unhandled fx push for namespace:" + namespace + " classpath:"+classFullName);
		}
	}
},

/*
	Provides a lookup of a class given a namespace.
	
	The following namespace formats are supported
	NSObject.NSSubClass.NSOtherSubClass
	
	or
	
	object.sub_class.other_sub_class
*/
classForNamespace:function(namespace) {
	if(namespace.startsWith("NSObject.")) {
		var lookup = namespace.replace("NSObject.", "");
		var parts = lookup.split(".");
		return fClass[parts[parts.length-1]];
	} else if(namespace.startsWith("object.")) {
		throw "Unsupported operation for +classForNamespace("+namespace+"), pretty dot syntex not supported yet.";
	}
	
	return null;
}
	
/*
	All subclasses can implement the +initalize class method, (they should never call super) 
	This gets called once per class during runtime initalization, it allowed for configureing class variables.,
	
	It does not get inherited by subclasses
	
	+initalize:function() {
		//Do nothing by default..
	}
*/

}});
//fClass.NSObject.extend = fClass._extend;			//Importent only for root object
//fClass.NSObject.proto = fClass.NSObject.alloc();	//ditto

/*
	NSModelObject
	====================================
	
	Provides a cached object store and integration with Froth Sever API
	
	Useage
	====================================
	
	Given a json {} object "obj"
	
	var real = fClass.ModelObjectSubclass.alloc().init(obj) (or null to create)
	-wrapps the json object with NSModelObject methods and froth functionality.
	
	
	//The Cache of object subclasses by objects[instance.className][instance.uid]
	
	
	Froth DOM Bindings & -didChangeValueForKey()
	============================================
	
	..provides bindings integration.. 
	
*/
// !NSModelObject
fClass.NSObject.extend("NSModelObject", { ivars: {
	uid:"temp"	
}, classMethods: {

//Called by this class's internal +extend class method when subclassing completed, this is never inherited
onDecended:function(decendent) {
	if(this.entities) {
		this.entities[decendent.entityName()] = decendent;
	}
},

initialize:function() {
	//We are only ever called once, so this is safe...
	this.objects = {};
	
	//An array of entity safe names for subclasses, ie XXThisClass = this_class, the value is the class
	this.entities = {};
},

//This is inherited to all Subclasses is MyObjectClass.registerObject(...)
registerObject:function(obj) {
	if(!fClass.NSModelObject.objects[obj.className]) {
				fClass.NSModelObject.objects[obj.className] = {};
				fClass.NSModelObject.objects[obj.className+'Array'] = [];
	}
	
	
	//Add to array
	if(!fClass.NSModelObject.objects[obj.className][obj.uid]) {
		fClass.NSModelObject.objects[obj.className+'Array'].push(obj);
	}
	
	//Add to dictionary
	fClass.NSModelObject.objects[obj.className][obj.uid] = obj;
},

//Look up an object by uid ? works on entitiy, ie fClass.MyClass.get(uid)
get:function(uid) {
	return fClass.NSModelObject.objects[this.className][uid];
},

//Convenience method to create and insert into context a model object given entity and optionally jsonObj
newForEntity:function(entity, obj) {
	return fClass.NSModelObject.entities[entity.toLowerCase()].alloc().init(obj);
},

//This allows for doing searchs based by entitiyname ie fClass.NSModelObject.entities[entity.toLowerCase()].get(...)
getByEntity:function(entity, uid) {
	var aclass = fClass.NSModelObject.entities[entity.toLowerCase()];
	
	if(!aclass) {
		NSErrorLog("NSModelObject +getByEntity error entity:"+entity+ " not found");
		return null;
	}

	return aclass.get(uid);
},

//Returns the entityName converted from the classname, if XXThisObject -> this_obect
entityName:function() {
	return this.className.substring(2).deCamelize();
},

/* The class us always converted to lower case upon reception. As sceala uses Capitals for classNames, and froth model uses lowercase for entity names
	of object is null, then this method Initializes a new object as considered created and returns it. It does not cache it as this
	is the responsability of the application layer 
 
	This method also looks for a change of fstate=deleted for deleting objects. This is object in not null.
 
	Subclasses can overide this, as the approach of consumers of this is to first check the object's class, then the object's super and on...
 */
handleScealaPush:function(className, tcobject, object) {
	
	var theClass = className.toLowerCase();
	var newObject;
	
	if(object && tcobject.attr("gindex") == object.uid) {
 		NSLog('Object exists locally for push');
 		newObject = object;
	} else {
		newObject = {};
	}
	
	newObject.uid = tcobject.attr("gindex");
	newObject.creator = tcobject.attr("creator").split("@")[0];
	newObject.modifier = tcobject.attr("lastmodifier").split("@")[0];
	newObject.created = new Date(parseInt(tcobject.attr("created")));		//Seconds since Jan 1, 1970
	newObject.modified = new Date(parseInt(tcobject.attr("modified")));		//..
	
	//Check if its a delete Local objects can mark fstate first to keep a recursive look from happining
	var pDel = tcobject.find("kvo[key=fstate]").text();
	if(pDel && pDel == 'deleted' && object && (!object.fstate || object.fstate != 'deleted')) {
		$(document).trigger('froth.model.'+className.toLowerCase()+'.delete', object);
		return null;
	}
	
	tcobject.find("kvo")
	.each(function() {
		  var ktype = $(this).attr("type");
		  var value = $(this).text();
		  
		  //Set the value's null state if ((null)) string
		  value = (value == '((null))')?null:value;
		  
		  var key = $(this).attr("key");
		  var isBool = false;
		  
		  if(value != null) {
			  if(ktype == "7") {
				value = new Date(parseInt(value)); //Seconds since jan 1, 1970
			  } else if(ktype == "2") {			   //represented as 0 || 1
					isBool = true;
					if(value == "1") {
						value = true;
					} else {
						value = false;
					}
			  } else if(ktype == "14") {
					value = value.split(",");
			  }
		  }
		  
		  var oldVal = newObject[key];
		  newObject[key] = value;
		  if(isBool || (object && ((oldVal && oldVal != value) || (!oldVal && value)))) {
				NSLog("will trigger -didChangeValueForKey for object class:"+ newObject.className+" uid:"+newObject.uid);
				newObject.didChangeValueForKey(key);
		  }
	});
	
	if(!object) {
		newObject = fClass.NSModelObject.newForEntity(theClass, newObject);	//Initialize the object with froth/its fClass
		
		//Send a create trigger
		$(document).trigger('froth.model.'+className.toLowerCase()+'.create', newObject);
	}
	
	return newObject;
}

}, methods: {

init:function(obj) {
	if(this.performSuper(this, "init")) {
		if(obj) {
			for(var key in obj) {
				if(typeof obj[key] != 'function') {
					this[key] = obj[key];
				}
			}
			
			if(this.uid != 'ignore' && !this.isPrototype) {
				/*if(!fClass.NSModelObject.objects[this.className])
					fClass.NSModelObject.objects[this.className] = {};
				
				fClass.NSModelObject.objects[this.className][this.uid] = this;*/
				this.registerObject(this);
			}
			
			//Setup modified/created dates
			if(this.modified && !(this.modified instanceof Date)) {
				var modified = obj.modified.replace('+0000', 'GMT');
				modified = modified.replace('0000', 'GMT');
				 
				this.modified = new Date.parse(modified); //Requires jsdate.js
			}
			if(this.created && !(this.created instanceof Date)) {
				var created = obj.created.replace('+0000', 'GMT');
				created = created.replace('0000', 'GMT');
			
				this.created = new Date.parse(created); //Requires jsdate.js
			}
		}
	}
	return this;
},

//We overide NSObject's -didChangeValueForKey for ui bindings support
didChangeValueForKey:function(key) {
	
	//Call super's impl
	this.performSuper(this, "didChangeValueForKey", arguments);
	
	
	/* Added the ability to do formattings with bkey='key/formatterName'
		Formatters are methods that must be implemented by froth objects
		for the system to work.
		
		if for key dueDate, a natuaral language formatter could be
		dueDateFormatterNameFormatter() {
		
		these functions must return strings
	 */
	var model = this.className;
	var uid = this.uid;
	
	var selector = ("[bnd='"+model+"/"+uid+"'][bkey*="+key+"]");
	
	//For this access
	var me = this;
	
	//Get the changlble attributes for the binding update
	var changed = $(selector)
	.each(function() {			  
		  var e = $(this);
		  var boundKeys = e.attr("bkey").split(" ");
		  var indexOfKey = $.inArray(key, boundKeys);	
		  
		  var elementProps = e.attr("bto").split(" ");
		  var updateProp = elementProps[indexOfKey];
		 
			  //add bfr for bound formatters for values, for no formatter use *
		  var formatter = null;
		  if(e.attr("bfr")) {
			 var formatters = e.attr("bfr").split(" ");
			 formatter = formatters[indexOfKey]=='*'?null:formatters[indexOfKey];
		  }
		  
		  var value = formatter?me[key+formatter+"Formatter"]():me[key];
		  
		  if(updateProp == 'text') {
			e.text(value);
		  } else if(updateProp == 'html') {
			e.html(value);
		  } else if(updateProp == 'val') {
			e.val(value);
		  } else if(updateProp.startsWith('attr')) {
			var attrName = updateProp.split("/")[1];
			e.attr(attrName, value);
		  } else if(updateProp == 'state') {
			if(value == true)
				e.attr("checked", "yes");
			else
				e.removeAttr("checked");
		  }
		  
		  e.trigger(key+".update.bind.froth", [key, me]);
	}).length;
	NSLog("BINDINGS++ "+ this.className + ".didChangeValueForKey('"+key+"') triggered "+changed+" changes");
}

}});


/*

	fClass Unit Testing
	==========================
	
	This provides the basis for frothjs libraries unit testing
	
	Each object should have a corresponding NSUnit.
	
	
	Testing Classes, instances and Methods
	======================================
	
	For rigorous testing, each class definition should have a corresponding subclass of NSUnit with a "Test" added
	to the end of the class definition name, this is a matter of style and not used by the system, as each class
	definition needs to include the class method

	+ (string)testClass {
		return NSObject...
	}
	
	Each method of a class can be tested with a method name that is the same as the test object, but include the
	prefix _test_theMethodName:function()...
	
	
	Any method that includes the _test_ prefix is ran though the test harness upon launch.	
	
	Runing Tests
	==============================
	
	To run a test, do the following
	
	fClass.NSUnit.performTests(); //<== class method
	
	This checks for all subclasses of NSUnit, and performs the tests associated with them
	
	To test a single unit, use fClass.MYUnitSubclass.performTests();
	
*/
// !NSUnit
fClass.NSObject.extend("NSUnit", { ivars: {
	fclass:{},			//<= Class of class to test, must be defined in subclasses
	initParams:null, 	//<= Initalization paramiters for the instance
	initName:"init"		//<= Name of desiganted initalizer for instance to test
}, classMethods : {

/*
	Class variables
	- tests
	- results (string log of last test)

*/

initialize:function() {
	this.tests = {};
	this.results = "";
},

//returns test results string
_performTest:function() {
	//init the test case
	var test = this.alloc().init();
	var resStr;
	
	test.setup();
	
	for(var method in test) {
		if(typeof test[method] == 'function' && method.startsWith("_test_")) {
			resString.append("--- "+ method +"---\n");
			
			var res = test[method]();
			if(res) {
				resString.append(res);
			} else {
				resString.append("- Passed -");
			}
			
			resString.append("--------------------")
		}
	}
	
	test.teardown();
	
	return resStr;
},

performTests:function() {
	//check if this == NSUnit, else perform a single test on this
	if(this.className === 'NSUnut') {
		this.results = "NSUnit test results \n===================";
	
		for(var test in this.tests) {
			this.results = this.results = "\n\n Test ["+test+"] \n================";
			this.results = this.results + test._performTest();
		}
		return this.results;
	} else {
		var result = this._performTest();
		NSLog(result);
		return result;
	}
}

}, methods : {

//Called for each test instace before tests start
setup:function() {
	//subclasses must call super's to insure we have a working instance object
	
	//prepare testing instance
	if(this.initParams) {
		var alloced = this.fclass.alloc();
		alloced[this.initName].apply(alloced, this.initParams);
	} else {
		this.fclass.alloc()[this.initName]();
	}
},

teardown:function() {
	//for subclasses
}

/* 	
	Tests should be written as so, return null for successful test, or "string" for failed test message 
	_test_init:function() {
		this.finst.init()...
		
		this.fclass //class to test, corresponds to finst as instance to test.
	}	
*/

}});


/*
	A simple fClass wrapper around jQueries .ajax impl

	Uses a delegate design for responce callbacks
*/
// !NSAjaxConnection
fClass.NSObject.extend("NSAjaxConnecton", { ivars : {

}, methods : {

}});

